;; midi-event.scm - (c) rohan drape, 2002-2005

;; A midi event is of the form (STATUS-BYTE [DATA-BYTES...]).

;; Type predicates for the four basic midi event types.  CHANNEL,
;; SYSEX and ARBITRARY events are transmissable and storable, META
;; events are defined for storage in midi files.

(define (midi-event-channel? e)
  (let ((status-byte (first e)))
    (and (>= status-byte #x80)
	 (<= status-byte #xEF))))

(define (midi-event-sysex? e)
  (let ((status-byte (first e)))
    (= status-byte #xF0)))

(define (midi-event-arbitrary? e)
  (let ((status-byte (first e)))
    (= status-byte #xF7)))

(define (midi-event-meta? e)
  (let ((status-byte (first e)))
    (= status-byte MC-meta-event)))

;; Generic predicate for META events.  The `event-type' is an
;; eight-bit integer.  It is important to note that the event length
;; is not explicitly stated in the lisp form.  This is permissable
;; since the lisp form allows the length to be determined easily.  It
;; is useful since the length value is stored as a variable length
;; integer, and as such is error prone to handle where not required.

(define (midi-meta-event-of-type? e event-type)
  (and (midi-event-meta? e)
       (= event-type (second e))))

;; Predicates for common META events.  The names are those given in
;; the published specification.

(define (midi-meta-event-sequence-number? e)
  (midi-meta-event-of-type? e MC-sequence-number))

(define (midi-meta-event-text-event? e)
  (midi-meta-event-of-type? e MC-text-event))

(define (midi-meta-event-copyright-notice? e)
  (midi-meta-event-of-type? e MC-copyright-notice))

(define (midi-meta--event-sequence-name? e)
  (midi-meta-event-of-type? e MC-sequence-name))

(define (midi-meta--event-intrument-name? e)
  (midi-meta-event-of-type? e MC-instrument-name))

(define (midi-meta--event-lyric? e)
  (midi-meta-event-of-type? e MC-lyric))

(define (midi-meta--event-marker? e)
  (midi-meta-event-of-type? e MC-marker))

(define (midi-meta-event-cue-point? e)
  (midi-meta-event-of-type? e MC-cue-point))

(define (midi-meta-event-midi-channel-prefix? e)
  (midi-meta-event-of-type? e MC-channel-prefix))

(define (midi-meta-event-end-of-track? e)
  (midi-meta-event-of-type? e MC-end-of-track))

(define (midi-meta-event-set-tempo? e)
  (midi-meta-event-of-type? e MC-set-tempo))

(define (midi-meta-event-smpte-offset? e)
  (midi-meta-event-of-type? e MC-smpte-offset))

(define (midi-meta-event-time-signature? e)
  (midi-meta-event-of-type? e MC-time-signature))

(define (midi-meta-event-key-signature? e)
  (midi-meta-event-of-type? e MC-key-signature))

(define (midi-meta-event-sequencer-specific? e)
  (midi-meta-event-of-type? e MC-sequencer-specific))

;; A constructor for time signature META events.  The input
;; `numerator' and `denominator' are written as in a score, ie. 4/4 or
;; 6/8.  The `pulse-at' input indicates the relation of the pulse and
;; is written as 1/4 for quarter-note (ie. crotchet) and so forth.
;; Time signatures are encoded in four bytes: nn dd cc bb.  The
;; denominator, 'dd', is encoded as an exponent of a base two integer.
;; The value 'cc' expresses the number of MIDI CLOCKS in a metronome
;; click.  There are in all cases twenty-four MIDI CLOCKS per quarter
;; note.  Therefore, to set the metronome to tick every eighth note
;; this value would be sixteen, to tick every dotted quarter note
;; thirty-two, and so forth.  The value 'bb' expresses the number of
;; notated thirty-second notes in a MIDI quarter note.  In all
;; ordinary cases this value is eight.

(defineK (encode-midi-meta-event-time-signature 
	  (numerator 4) (denominator 4) (pulse-at 1/4) (bb #x08))
  (define (make-dd denominator)
    (case denominator
      ((1) 0)
      ((2) 1)
      ((4) 2)
      ((8) 3)
      ((16) 4)
      ((32) 5)
      (else (error "Illegal denominator" denominator))))
  (list MC-meta-event MC-time-signature
	numerator (make-dd denominator)
	(roundE (* pulse-at #x60)) bb))

;; Midi META event constructors.

(define (encode-midi-meta-event-end-of-track)
  (list MC-meta-event MC-end-of-track))

(define (encode-midi-meta-event-text-event text)
  (append (list MC-meta-event MC-text-event)
	  (map char->integer (string->list text))))

;; Returns the integer value of the Set Tempo event.  The tempo is
;; stored in usecs per quarter note (upq).  The standard user level
;; notation is in quarter notes per minute (qpm).

(define (decode-midi-meta-event-set-tempo e)
  (midi-upq-to-qpm (byte-list->integer (sublist e 2 5))))

;; Return the midi META event to set the tempo to `qpm', quarter-notes
;; per minute.

(define (encode-midi-meta-event-set-tempo qpm)
  (append (list MC-meta-event MC-set-tempo)
	  (integer->byte-list (midi-qpm-to-upq qpm) 3)))
